#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <mpi.h>
#include <omp.h>
#include <string.h>
#include "trie.c"

#define nPalavras 5000

int main(int argc, char *argv[]){
	int i = 0, j = 0, count1 = 0, count2 = 0, randomNum, bytes, nameLen, flag;
	long lenArc;
	char word[10],randomWord[6], *text;
	char palavras[nPalavras][6];
	void *valor = 1;
	FILE *fileWord;
	int flagFind = -1,itsOver = 0;
	char processorName[MPI_MAX_PROCESSOR_NAME];
	double porcent = 0.0, tempoi = 0.0, tempof;
	MPI_Request overReq;
	int numRank, numProc;

	MPI_Status status;
    	MPI_Init(&argc, &argv);
    	MPI_Comm_rank(MPI_COMM_WORLD, &numRank); 
    	MPI_Comm_size(MPI_COMM_WORLD, &numProc);
	MPI_Get_processor_name(processorName, &nameLen);

	printf("Nome do processador: %s | rank: %d\n", processorName, numRank);

	if(numRank == 0){ //Processo Mestre

	Trie *tree; //Criando estrutura que ira armazenar as palavras
    	tree = trie_new();
	tempoi = MPI_Wtime(); //Obtendo tempo inicial da leitura

    	if((fileWord = fopen("palavras.txt","r")) == NULL){
    		printf("Erro. Arquivo nao encontrado.\n");		
    		exit(-1);
    	}
		fseek(fileWord,0,SEEK_END);
		lenArc = ftell(fileWord); //Obtendo tamanho do arquivo
		rewind(fileWord);
		text = (char *)malloc(sizeof(char)*lenArc);

		bytes = fread(text,sizeof(char),lenArc,fileWord);
	
		for(i = 0; i < bytes;i++){
			word[j] = '\0';
			if(isalpha(text[i]) && strlen(word) < 5){ //Caso a palavra ainda nao tenho 5 letras
			    	word[j] = text[i];
			    	j++;
			}
			else{
				word[j+1] = '\0';
				if(!trie_lookup(tree,word) && strlen(word) > 1){//Caso a palavra nao esteja adicionada na estrutura, e possua pelo menos um digito                	
					trie_insert(tree,word,valor);
				}
		       		j = 0;
		       		count1++;
				if(isalpha(text[i])) //Caso a palavra seja dividida
					i--;
			}
		}	
		
		fclose(fileWord);
		tempof = MPI_Wtime(); //Obtendo tempo final da leitura
		printf("Tempo para leitura e organização das palavras: %lf\n", tempof - tempoi);
		count1 = trie_num_entries(tree);
		for(i=0; i < nPalavras; i++)
			palavras[i][0] = '\0';
		tempoi = MPI_Wtime(); //Obtendo tempo inicial da geracao de palavras aleatorias
		while(!itsOver){
			omp_set_num_threads(6);
			#pragma omp parallel for private(i)
			for(i=0; i < nPalavras; i++){
				if(trie_lookup(tree, palavras[i])){ 
					#pragma omp critical
					count2++;
					trie_remove(tree,palavras[i]);
				}
			}
			tempof = MPI_Wtime();
			if((double)((double)count2/(double)count1) >= porcent){
				printf("%.0lf%% do arquivo gerado - %d/%d\n", porcent*100, count2, count1);
				printf("%.10lf segundos ate o momento\n", tempof - tempoi);
				porcent += 0.05;
			}

			if(!trie_num_entries(tree) || ((tempof - tempoi) > 60.0))
				itsOver = 1;
			MPI_Recv(palavras,nPalavras*6,MPI_CHAR, MPI_ANY_SOURCE, MPI_ANY_TAG,MPI_COMM_WORLD,&status); //Recebe uma matriz com nPalavras palavras geradas por um no qualquer, que ja esteja pronto
		}
		trie_free(tree);
		printf("%.0lf%% do arquivo foi gerado - %d/%d\n", ((double)count2/count1)*100, count2, count1);
		printf("Em %.10lf segundos.\n", tempof - tempoi);
		for(i=0; i<numProc ;i++)
			MPI_Send(&itsOver, 1, MPI_INT, i, 100, MPI_COMM_WORLD);

		
	}else if(numRank != 0){

		srand(time(NULL) + numRank);
		MPI_Irecv(&count1, 1, MPI_INT, 0, 100, MPI_COMM_WORLD, &overReq); //Receive de controle, responsavel por finalizar o processo quando o no mestre terminar

		do{
			int c;
			omp_set_num_threads(2);
			#pragma omp parallel for private(c)
			for(c=0; c < nPalavras; c+=5){ //Gerando palavras aleatorias
				randomWord[0] = '\0';
			   	for(i = 0; i < 5; i++){ //Gera palavras com comprimentos cada vez maiores, ate o limite de 5
				       	randomNum = 26 * (rand() / (RAND_MAX + 1.0));

					randomNum = randomNum + 97;
					if(randomNum == 123) randomNum = 122;

					randomWord[i] = (char) randomNum;
					randomWord[i+1] = '\0';
					strcpy(palavras[c+i], randomWord);
			    	}
			}
			MPI_Test(&overReq, &flag, &status); //Verifica se o processo mestre ja enviou o sinal de final de programa
			if(flag)
				itsOver = 1;
			else
				MPI_Send(palavras, nPalavras*6, MPI_CHAR, 0, numRank, MPI_COMM_WORLD); //Envia as palavras geradas
		}while(!itsOver);

	}
	MPI_Finalize();

    return 0;
}
